home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connectio…eloper Series 2005 March / Dev.CD Mar 05.iso / What's New / Sample Code / AppleScriptRunner / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  2005-01-19  |  30.5 KB  |  731 lines

  1. /*
  2.     File:        main.c
  3.  
  4.     Abstract:    A skeleton of modern nib-based and Carbon Events-based Carbon application.
  5.                 AppleScriptRunner contains a folder within its application bundle, "AppleScripts"
  6.                 which contains a number of AppleScripts.  AppleScriptRunner builds a popup menu
  7.                 populated with the AppleScript names.  When "Execute" is clicked, the selected
  8.                 AppleScript is executed passing it one Text parameter, the contents of out HITextView.
  9.                 In this example all AppleScripts must take one, and only one, text parameter. An
  10.                 advisable method of adding functionallity to an application to send email is to 
  11.                 create an AppleScript to send email, and then envoke the script from the application.
  12.                 In this example "Entourage" and "Mail" each send email, provided the email clients are
  13.                 correctly configured.  The format of the HITextView to send mail should be:
  14.                 "Santa@NorthPole.com Dear Santa, My chimney ..."
  15.                 Because this sample requires only one input parameter, the AppleScrips themselves parse
  16.                 the "To" email address out as the first word, and assume the rest is the message body.
  17.                 The main entry to the "interesting" portion of the code is the routine: RunAppleScript().
  18.                 
  19.     Version:    1.0
  20.  
  21.     Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  22.                 ("Apple") in consideration of your agreement to the following terms, and your
  23.                 use, installation, modification or redistribution of this Apple software
  24.                 constitutes acceptance of these terms.  If you do not agree with these terms,
  25.                 please do not use, install, modify or redistribute this Apple software.
  26.  
  27.                 In consideration of your agreement to abide by the following terms, and subject
  28.                 to these terms, Apple grants you a personal, non-exclusive license, under Apple's
  29.                 copyrights in this original Apple software (the "Apple Software"), to use,
  30.                 reproduce, modify and redistribute the Apple Software, with or without
  31.                 modifications, in source and/or binary forms; provided that if you redistribute
  32.                 the Apple Software in its entirety and without modifications, you must retain
  33.                 this notice and the following text and disclaimers in all such redistributions of
  34.                 the Apple Software.  Neither the name, trademarks, service marks or logos of
  35.                 Apple Computer, Inc. may be used to endorse or promote products derived from the
  36.                 Apple Software without specific prior written permission from Apple.  Except as
  37.                 expressly stated in this notice, no other rights or licenses, express or implied,
  38.                 are granted by Apple herein, including but not limited to any patent rights that
  39.                 may be infringed by your derivative works or by other works in which the Apple
  40.                 Software may be incorporated.
  41.  
  42.                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  43.                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  44.                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  45.                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  46.                 COMBINATION WITH YOUR PRODUCTS.
  47.  
  48.                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  49.                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  50.                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  51.                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  52.                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  53.                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  54.                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  55.  
  56.     Copyright © 2005 Apple Computer, Inc., All Rights Reserved
  57. */
  58.  
  59. #include    <Carbon/Carbon.h>
  60. #include    <sys/param.h>
  61.  
  62. struct  GlobalAppInfo                            //  Application globals
  63. {
  64.     IBNibRef                mainNibRef;
  65. };
  66. typedef struct GlobalAppInfo GlobalAppInfo;
  67.  
  68. static    void        SendCommandProcessEvent( UInt32 commandID );
  69. static  OSStatus    GetControlBySigAndID( WindowRef window, OSType  signature, SInt32 id, ControlRef *control );
  70. static    void        InstallAppleEventHandlers( void );
  71. static    void        DoPreferences( void );
  72. static  OSStatus    DoSomething();
  73. static    OSStatus    NavOpenDocument();
  74. static    pascal        Boolean NavOpenFilterProc( AEDesc *theItem, void *info, NavCallBackUserData callBackUD, NavFilterModes filterMode );
  75. static    OSStatus    HIViewFindBySigAndID( HIViewRef inStartView, OSType signature, SInt32 id, HIViewRef *outControl );
  76. static    TXNObject    GetTXNObjectBySigAndID( WindowRef window, OSType signature, SInt32 id );
  77. static    void        Wprintf( WindowRef window, char const *fmt, ... );
  78. static  OSStatus    DisplayTextFileInWindow( WindowRef window, FSRef *fsRef );
  79. static    pascal  OSStatus    CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData );    //  Added kHICommandRevert, kHICommandPageSetup, and kHICommandPrint
  80. static    OSStatus    OpenDocuments( AEDescList docList );    //  Open Text files
  81. static  OSStatus    RunAppleScript( FSRef *scriptFSRef, char *textParameter );
  82. static  OSStatus    GetAppleScriptsFolderFSRef( FSRef *fsRef);
  83. static  OSStatus    CreateMessageEvent( AppleEvent *theEvent, char *parameter );
  84. static    pascal    OSStatus    MainWindowEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData );
  85. static    OSStatus    DoNewWindow( WindowRef *window );        //  Install Event Handler for Text view
  86.  
  87.  
  88. GlobalAppInfo        g;
  89.  
  90.  
  91. #pragma mark -
  92. #pragma mark • Main •
  93.  
  94. int    main( int argc, char *argv[] )
  95. {
  96.     OSStatus                status;
  97.     long                    response;
  98.     const   EventTypeSpec   commandProcessEvents[]        = { { kEventClassCommand, kEventCommandProcess } };
  99.  
  100.     BlockZero( &g, sizeof(g) );
  101.  
  102.     status    = Gestalt( gestaltSystemVersion, &response );
  103.     if ( ! ((status == noErr) && (response >= 0x00001030)) )            //    We require Mac OS X 10.3 or greater to run
  104.     {
  105.         DialogRef    alertDialod;
  106.         CreateStandardAlert( kAlertStopAlert, CFSTR("Mac OS X 10.3 (minimum) is required for this application"), NULL, NULL, &alertDialod );
  107.         RunStandardAlert( alertDialod, NULL, NULL );
  108.         ExitToShell();
  109.     }
  110.  
  111.     //    Create a Nib reference passing the name of the nib file (without the .nib extension) CreateNibReference only searches into the application bundle.
  112.     status    = CreateNibReference( CFSTR("main"), &g.mainNibRef );
  113.     require_noerr( status, CantGetNibRef );
  114.  
  115.     //    Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar object. This name is set in InterfaceBuilder when the nib is created.
  116.     status    = SetMenuBarFromNib( g.mainNibRef, CFSTR("MenuBar") );
  117.     require_noerr( status, CantSetMenuBar );
  118.  
  119.     EnableMenuCommand( NULL, kHICommandPreferences );                    //    Enable Preferences menu item
  120.  
  121.     InstallAppleEventHandlers();
  122.     InstallApplicationEventHandler( NewEventHandlerUPP(CommandProcessEventHandler), GetEventTypeCount(commandProcessEvents), commandProcessEvents, NULL, NULL );
  123.  
  124.     RunApplicationEventLoop();                                            //    Call the event loop
  125.     
  126. CantSetMenuBar:
  127. CantGetNibRef:
  128.     return( status );
  129. }
  130.  
  131.  
  132. #pragma mark -
  133. #pragma mark • AppleEvent Handlers •
  134.  
  135. static    pascal    OSErr    HandleAppleEventOapp( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon )
  136. {
  137.     SendCommandProcessEvent( kHICommandNew );                            // Instantiate an empty window at the beginning so the User sees something
  138.     return( noErr );
  139. }
  140.  
  141.  
  142. static    pascal    OSErr    HandleAppleEventRapp( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon )
  143. {
  144.     WindowRef    window    = GetFrontWindowOfClass( kDocumentWindowClass, true );
  145.     if ( window == NULL )
  146.         SendCommandProcessEvent( kHICommandNew );                        //    We were already running but with no windows so we create an empty one.
  147.     return( noErr );
  148. }
  149.  
  150. static    pascal    OSErr    HandleAppleEventOdoc( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon )
  151. {
  152.     AEDescList        docList;
  153.     OSErr            err            = AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &docList );
  154.     require_noerr( err, CantGetDocList );
  155.  
  156.     err    = OpenDocuments( docList );
  157.     AEDisposeDesc( &docList );
  158.  
  159. CantGetDocList:
  160.       return( err );
  161. }
  162.  
  163. static    pascal    OSErr    HandleAppleEventPdoc( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon )
  164. {
  165.     return( errAEEventNotHandled );
  166. }
  167.  
  168. static    void    InstallAppleEventHandlers( void )
  169. {
  170.     OSErr        status;
  171.     
  172.     status    = AEInstallEventHandler( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP(HandleAppleEventOapp), 0, false );    require_noerr( status, CantInstallAppleEventHandler );
  173.     status    = AEInstallEventHandler( kCoreEventClass, kAEReopenApplication, NewAEEventHandlerUPP(HandleAppleEventRapp), 0, false );    require_noerr( status, CantInstallAppleEventHandler );
  174.     status    = AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(HandleAppleEventOdoc), 0, false );        require_noerr( status, CantInstallAppleEventHandler );
  175.     status    = AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerUPP(HandleAppleEventPdoc), 0, false );    require_noerr( status, CantInstallAppleEventHandler );
  176.     //    Note: Since RunApplicationEventLoop installs a Quit AE Handler, there is no need to do it here.
  177.  
  178. CantInstallAppleEventHandler:
  179.     return;
  180. }
  181.  
  182.  
  183. #pragma mark -
  184. #pragma mark • CarbonEvent Handlers •
  185.  
  186. static    pascal    OSStatus    CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData )
  187. {
  188.     HICommand        command;
  189.     UInt32            secs;
  190.     DateTimeRec        date;
  191.     OSStatus        status            = eventNotHandledErr;
  192.     TXNObject        txnObject        = GetTXNObjectBySigAndID( GetFrontWindowOfClass(kDocumentWindowClass, true), 'HITv', 0 );
  193.  
  194.     GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command );
  195.  
  196.     switch ( command.commandID )
  197.     {
  198.         case kHICommandNew:
  199.             status  = DoNewWindow( NULL );
  200.             GetDateTime( &secs );                                                //  Demonstrate how to use utility routine Wprintf
  201.             SecondsToDate( secs, &date );
  202.             Wprintf( GetFrontWindowOfClass(kDocumentWindowClass, true), "Todays Date: %d/%d/%d\n", date.month, date.day, date.year );
  203.             break;
  204.         
  205.         case kHICommandOpen:
  206.             status    = NavOpenDocument();
  207.             break;
  208.             
  209.         case kHICommandPreferences:
  210.             DoPreferences();
  211.             break;
  212.  
  213.         case kHICommandRevert:
  214.             if ( txnObject != NULL ) status = TXNRevert( txnObject );
  215.             break;
  216.         
  217.         case kHICommandPageSetup:
  218.             if ( txnObject != NULL ) status = TXNPageSetup( txnObject );
  219.             status  = noErr;
  220.             break;
  221.         
  222.         case kHICommandPrint:
  223.             if ( txnObject != NULL ) status = TXNPrint( txnObject );
  224.             status  = noErr;
  225.             break;
  226.         
  227.         case 'DoIt':                                                            //  Our own menu to hook in and Do Something...
  228.             status  = DoSomething();
  229.             break;
  230.         }
  231.  
  232.     return( status );
  233. }
  234.  
  235. static    pascal    OSStatus    MainWindowEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *inUserData )
  236. {
  237.     #pragma unused ( inCallRef )
  238.     HICommand        command;
  239.     OSStatus        status            = eventNotHandledErr;
  240.     UInt32            eventKind        = GetEventKind( inEvent );
  241.     UInt32            eventClass        = GetEventClass( inEvent );
  242.     WindowRef        window            = (WindowRef) inUserData;
  243.  
  244.     switch ( eventClass )
  245.     {
  246.         case kEventClassWindow:
  247.             //if ( eventKind == kEventWindowClose )                                //  Dispose extra window storage here
  248.             break;
  249.             
  250.         case kEventClassCommand:
  251.             if ( eventKind == kEventCommandProcess )
  252.             {
  253.                 GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command );
  254.                 if ( command.commandID == kHICommandOK )                        //    OK (Execute) Button was clicked in window
  255.                 {
  256.                     FSRef                        parentFSRef;
  257.                     FSRef                        fsRef;
  258.                     TXNObject                    txnObject;
  259.                     CFStringRef                    cfString;
  260.                     HFSUniStr255                appleScriptName;
  261.                     ControlRef                    control;
  262.                     SInt32                        controlValue;
  263.                     MenuRef                        menuRef;
  264.                     Handle                        txnDataHandle        = NULL;
  265.                     
  266.                     //  Get the text to pass as a parameter to the AppleScript
  267.                     txnObject    = GetTXNObjectBySigAndID( window, 'HITv', 0 );                                //    Get the TXNObject
  268.                     status        = TXNGetDataEncoded( txnObject, kTXNStartOffset, kTXNEndOffset, &txnDataHandle, kTXNTextData );        //    Retrieve the text. kTXNTextData specifies Text, not Unicode or Rich Text
  269.  
  270.                     //  Find the name of the chosen menu item, which is the name of our AppleScript to run
  271.                     status  = GetControlBySigAndID( window, 'PopM', 0, &control );                            //    Get the popup menu control
  272.                     controlValue    = GetControl32BitValue( control );                                        //    Get the control value, which is the selected menu item
  273.                     menuRef    = GetControlPopupMenuHandle( control );                                            //    Get the MenuRef from the control
  274.                     CopyMenuItemTextAsCFString( menuRef, controlValue, &cfString );                            //    Copy the selected item text into a CFString
  275.                     
  276.                     //    Convert our CFString to an HFSUniStr255 and create an FSRef to our chosen AppleScript
  277.                     appleScriptName.length = CFStringGetBytes( cfString, CFRangeMake(0, MIN(CFStringGetLength(cfString), 255)), kCFStringEncodingUnicode, 0, false, (UInt8 *)(appleScriptName.unicode), 255, NULL );
  278.                     status    = GetAppleScriptsFolderFSRef( &parentFSRef );                                    //    Get FSRef to AppleScripts folder
  279.                     status    = FSMakeFSRefUnicode( &parentFSRef, appleScriptName.length, appleScriptName.unicode, kTextEncodingUnknown, &fsRef );   //    FSMakeFSRefUnicode to make FSRef to the selected AppleScript
  280.  
  281.                     //    Run the AppleScript passing in our text as a parameter
  282.                     status  = RunAppleScript( &fsRef, *txnDataHandle );
  283.                     
  284.                     //    Release what we have allocated
  285.                     if ( cfString != NULL )    CFRelease( cfString );
  286.                     if ( txnDataHandle != NULL )    DisposeHandle( txnDataHandle );
  287.                 }
  288.             }
  289.             break;
  290.     }
  291.     
  292.     return( status );
  293. }
  294.  
  295. #pragma mark -
  296. #pragma mark • Windows •
  297.  
  298. static    void    DoPreferences( void )
  299. {                                                                                //  Entry point for a preferences window
  300.     DialogRef        theAlert;
  301.     CreateStandardAlert( kAlertStopAlert, CFSTR("No Preferences yet!"), NULL, NULL, &theAlert );
  302.     RunStandardAlert( theAlert, NULL, NULL );
  303. }
  304.  
  305.  
  306. static    OSStatus    DoNewWindow( WindowRef *outWindow )
  307. {
  308.     OSStatus                    status;
  309.     static    EventHandlerUPP        windowEventHandlerUPP;
  310.     const    EventTypeSpec        windowEvents[]        =    {    { kEventClassCommand, kEventCommandProcess }, { kEventClassWindow, kEventWindowClose }    };
  311.     WindowRef                    window                = NULL;
  312.  
  313.     //    Create a window. "MainWindow" is the name of the window object. This name is set in    InterfaceBuilder when the nib is created.
  314.     status    = CreateWindowFromNib( g.mainNibRef, CFSTR("MainWindow"), &window );
  315.     require_noerr( status, CantCreateWindow );
  316.  
  317.     if ( windowEventHandlerUPP == NULL ) windowEventHandlerUPP    = NewEventHandlerUPP( MainWindowEventHandler );
  318.     status    = InstallWindowEventHandler( window, windowEventHandlerUPP, GetEventTypeCount(windowEvents), windowEvents, window, NULL );
  319.     require_noerr( status, CantInstallWindowEventHandler );
  320.  
  321.     {
  322.         FSIterator                    iterator;
  323.         ItemCount                    actualObjects;
  324.         FSRef                        fsRefs[32];                                            //    Allow up to 32 FSRefs, in this case 32 AppleScripts
  325.         FSRef                        fsRef;
  326.         CFStringRef                    cfString;
  327.         ControlRef                    control;
  328.         int                            i;
  329.         MenuRef                        menuRef;
  330.  
  331.         status  = GetAppleScriptsFolderFSRef( &fsRef );                                    //    Locate our AppleScripts folder within our application bundle
  332.         status  = FSOpenIterator( &fsRef, kFSIterateFlat, &iterator );                    //    Read the first 32 items of the "AppleScripts" folder using FSOpenIterator-FSGetCatalogInfoBulk-FSCloseIterator
  333.         require_noerr( status, FileSystemError );
  334.         status  = FSGetCatalogInfoBulk( iterator, 32, &actualObjects, NULL, 0, NULL, fsRefs, NULL, NULL );
  335.         status  = FSCloseIterator( iterator );
  336.         if ( actualObjects > 0 )                                                        //    If we found some items
  337.         {
  338.             status  = GetControlBySigAndID( window, 'PopM', 0, &control );                //    Get the popup menu control
  339.             menuRef    = GetControlPopupMenuHandle( control );                                //    Get the MenuRef from the control
  340.             SetControl32BitMaximum( control, actualObjects );                            //    Need to SetControl32BitMaximum for popups to function as expected
  341.             DeleteMenuItem( menuRef, 1 );                                                //    Delete the first menuItem titled: "No AppleScripts Found"
  342.             for ( i = 0 ; i < actualObjects ; i++ )                                        //    Itterate over the files we found
  343.             {
  344.                 status    = LSCopyDisplayNameForRef( &fsRefs[i], &cfString );                //    Get the name of the file the Finder displays (may not include file extension)
  345.                 CFShow( cfString );
  346.                 
  347.                 if ( (status == noErr) && ( ( CFStringHasPrefix( cfString, CFSTR(".") ) ) == false ) )  //  Don't add ".DS_Store", or any other file starting with a "."
  348.                     AppendMenuItemTextWithCFString( menuRef, cfString, 0, NULL, NULL );    //    Append the CFString of the AppleScript to out popup menu
  349.             }
  350.         }
  351.     }
  352.  
  353.     //    The window was created hidden so show it if the window parameter is NULL, if it's not, it will be the responsibility of the caller to show it.
  354.     if ( outWindow == NULL )    ShowWindow( window );
  355.  
  356.     SetWindowModified( window, false );
  357.     
  358. FileSystemError:
  359. CantInstallWindowEventHandler:
  360. CantCreateWindow:
  361.     if ( outWindow != NULL )    *outWindow    = window;
  362.     return( status );
  363. }
  364.  
  365.  
  366. #pragma mark -
  367. #pragma mark • Save/Open Document •
  368.  
  369. static    OSStatus    NavOpenDocument()
  370. {
  371.     OSStatus                    status;
  372.     NavDialogCreationOptions    options;
  373.     NavReplyRecord                navReply;
  374.     static    NavObjectFilterUPP    navFilterUPP;
  375.     NavDialogRef                navDialog        = NULL;
  376.  
  377.     status    = NavGetDefaultDialogCreationOptions( &options );
  378.     require_noerr( status, CantGetDefaultOptions );
  379.  
  380.     if ( navFilterUPP == NULL )    navFilterUPP = NewNavObjectFilterUPP( NavOpenFilterProc );        //    Filter only documents we can open
  381.  
  382.     status    = NavCreateChooseFileDialog( &options, NULL, NULL, NULL, navFilterUPP, NULL, &navDialog );
  383.     require_noerr( status, CantCreateDialog );
  384.     
  385.     status    = NavDialogRun( navDialog );
  386.     require_noerr( status, CantRunDialog );
  387.     
  388.     status    = NavDialogGetReply( navDialog, &navReply );
  389.     require( (status == noErr) || (status == userCanceledErr), CantGetReply );
  390.  
  391.     if ( navReply.validRecord )    status    = OpenDocuments( navReply.selection );
  392.     else                        status    = userCanceledErr;
  393.  
  394.     NavDisposeReply( &navReply );
  395.  
  396. CantGetReply:
  397. CantRunDialog:
  398.     if ( navDialog != NULL )    NavDialogDispose( navDialog );
  399. CantCreateDialog:
  400. CantGetDefaultOptions:
  401.     return( status );
  402. }
  403.  
  404.  
  405. //    Generic Navigation Services filter proc described in TechNote 2017, "Using Launch Services"
  406. //    This filter querries LaunchServices to ask what types of documents this application can open.
  407. //    Openable document types are defined within the applications info.plist file.
  408. static    pascal    Boolean    NavOpenFilterProc( AEDesc *theItem, void *info, NavCallBackUserData callBackUD, NavFilterModes filterMode )
  409. {
  410.     LSItemInfoRecord        lsInfoRec;
  411.     FSRef                    fsRef;
  412.     OSStatus                status;
  413.     static    Boolean            applicationFSRefInitialized;
  414.     static    FSRef            applicationFSRef;
  415.     Boolean                    canViewItem                = false;
  416.     
  417.     if ( theItem->descriptorType == typeFSRef )
  418.     {
  419.         status    = AEGetDescData( theItem, &fsRef, sizeof(fsRef) );
  420.         require_noerr( status, CantGetFSRef );
  421.         
  422.         status    = LSCopyItemInfoForRef( &fsRef, kLSRequestAllInfo, &lsInfoRec );    //    Ask LaunchServices for information about the item
  423.         require( (status == noErr) || (status == kLSApplicationNotFoundErr), LaunchServicesError );
  424.         
  425.         if ( applicationFSRefInitialized == false )                                    //  First time this routine ir run, we locate our application bundle
  426.         {
  427.             ProcessSerialNumber        psn            = { 0, kCurrentProcess };
  428.             GetProcessBundleLocation( &psn, &applicationFSRef );                    //  Save the reference in a static
  429.             applicationFSRefInitialized    = true;
  430.         }
  431.         
  432.         if ( (lsInfoRec.flags & kLSItemInfoIsContainer) != 0 )                        //  If it's a folder, make it selectable
  433.             canViewItem    = true;
  434.         else
  435.             status    = LSCanRefAcceptItem( &fsRef, &applicationFSRef, kLSRolesViewer, kLSAcceptDefault, &canViewItem );        //  Can this application "view" this file?
  436.     }
  437.  
  438. LaunchServicesError:
  439. CantGetFSRef:
  440.     return( canViewItem );
  441. }
  442.  
  443.  
  444. static    OSStatus    OpenDocuments( AEDescList docList )
  445. {
  446.     long                index;
  447.     FSRef                fsRef;
  448.     CFStringRef            fileName;
  449.     WindowRef            window            = NULL;
  450.     long                count            = 0;
  451.     OSStatus            status            = AECountItems( &docList, &count );
  452.     require_noerr( status, CantGetCount );
  453.  
  454.     for( index = 1; index <= count; index++ )
  455.     {
  456.         if ( (status = AEGetNthPtr( &docList, index, typeFSRef, NULL, NULL, &fsRef, sizeof(FSRef), NULL) ) == noErr )
  457.         {
  458.             status    = DoNewWindow( &window );                                        //    Create a new (Hidden) window
  459.             require_noerr( status, CantCreateWindow );
  460.  
  461.             status    = LSCopyDisplayNameForRef( &fsRef, &fileName );                    //    Get the name of the file the Finder displays (may not include file extension)
  462.             require_noerr( status, CantGetName );
  463.             SetWindowTitleWithCFString( window, fileName );                            //    Set the Window title
  464.             
  465.             status  = DisplayTextFileInWindow( window, &fsRef );
  466.             if ( status == noErr )
  467.                 ShowWindow( window );                                                //    The window was created hidden so show it
  468.             else
  469.                 DisposeWindow( window );
  470.         }
  471.     }
  472.     
  473. CantGetName:
  474. CantCreateWindow:
  475. CantGetCount:
  476.     return( status );
  477. }
  478.  
  479. #pragma mark -
  480. #pragma mark • Utilities •
  481.  
  482. //    Utility routine to synchronously send a basic "kEventClassCommand / kEventCommandProcess" CarbonEvent to the application target
  483. static  void    SendCommandProcessEvent( UInt32 commandID )
  484. {
  485.     HICommand                command;
  486.     EventRef                event;
  487.  
  488.     BlockZero( &command, sizeof(command) );
  489.     command.commandID    = commandID;
  490.     
  491.     (void) CreateEvent( NULL,  kEventClassCommand, kEventCommandProcess, GetCurrentEventTime(), kEventAttributeUserEvent, &event );
  492.     (void) SetEventParameter( event, kEventParamDirectObject, typeHICommand, sizeof(command), &command );
  493.     (void) SendEventToApplication( event );
  494.     (void) ReleaseEvent( event );
  495. }
  496.  
  497.  
  498. static  OSStatus    GetControlBySigAndID( WindowRef window, OSType  signature, SInt32 id, ControlRef *control )
  499. {
  500.     ControlID    controlID    = { signature, id };
  501.     return( GetControlByID( window, &controlID, control ) );
  502. }
  503.  
  504. static    OSStatus    HIViewFindBySigAndID( HIViewRef inStartView, OSType signature, SInt32 id, HIViewRef *outControl )
  505. {
  506.     HIViewID    hiViewID    = { signature, id };
  507.     return( HIViewFindByID( inStartView, hiViewID, outControl ) );
  508. }
  509.  
  510.  
  511. static    TXNObject    GetTXNObjectBySigAndID( WindowRef window, OSType signature, SInt32 id )
  512. {
  513.     HIViewRef        hiTextView;
  514.     TXNObject        txnObject        = NULL;
  515.     
  516.     if ( window != NULL )
  517.         if ( HIViewFindBySigAndID( HIViewGetRoot(window), signature, id, &hiTextView ) == noErr )
  518.             if ( hiTextView != NULL )
  519.                 txnObject    = HITextViewGetTXNObject( hiTextView );
  520.     
  521.     return( txnObject );
  522. }
  523.  
  524. /*****************************************************
  525. *
  526. * ExecuteCompiledAppleScriptEvent( AEDesc *scriptData, AppleEvent *theEvent, AEDesc *resultData )
  527. *
  528. * Purpose:  Generic routine to execute our AppleScriptEvent, passing parameters to an
  529. *            AppleScript running inside my application
  530. *
  531. * Notes:    http://developer.apple.com/qa/qa2001/qa1111.html
  532. *
  533. * Inputs:   scriptData        - Reference to the AppleScript to be executed
  534. *            theEvent        - text parameter to our AppleScript as an AppleEvent
  535. *            resultData        - result from script 
  536. *
  537. * Returns:  OSStatus        - error code (0 == no error) 
  538. */
  539. OSStatus    ExecuteCompiledAppleScriptEvent( AEDesc *scriptData, AppleEvent *theEvent, AEDesc *resultData )
  540. {
  541.     OSStatus            err;
  542.     ComponentInstance    theComponent    = NULL;
  543.     OSAID                contextID        = kOSANullScript;
  544.     OSAID                resultID        = kOSANullScript;
  545.  
  546.     theComponent    = OpenDefaultComponent( kOSAComponentType, typeAppleScript );    //    Open the scripting component
  547.     if ( theComponent == NULL )    { err = paramErr; goto Bail; }
  548.  
  549.     err    = OSALoad( theComponent, scriptData, kOSAModeNull, &contextID );            //    Compile the script into a new context
  550.     require_noerr( err, Bail );
  551.  
  552.     err    = OSAExecuteEvent( theComponent, theEvent, contextID, kOSAModeNull, &resultID );    //    Run the script
  553.  
  554.     if ( resultData != NULL )                                                        //    Collect the results - if any
  555.     {
  556.         AECreateDesc( typeNull, NULL, 0, resultData );
  557.         if ( err == errOSAScriptError )
  558.             OSAScriptError( theComponent, kOSAErrorMessage, typeChar, resultData );
  559.         else if ( (err == noErr) && (resultID != kOSANullScript) )
  560.             OSADisplay(theComponent, resultID, typeChar, kOSAModeNull, resultData);
  561.     }
  562.  
  563. Bail:
  564.     if ( contextID != kOSANullScript )    OSADispose( theComponent, contextID );
  565.     if ( resultID != kOSANullScript )    OSADispose( theComponent, resultID );
  566.     if ( theComponent != NULL )            CloseComponent( theComponent );
  567.     return( err );
  568. }
  569.  
  570. #pragma mark -
  571. #pragma mark • Specialized Routines •
  572.  
  573. static  OSStatus    DoSomething()
  574. {
  575.     DebugStr( "\pDoSomething Was Called\n" );
  576.     return( noErr );
  577. }
  578.  
  579. //    This utility routine takes printf style arguments and puts the resulting text in the HITextView ( 'HITv', 0 ) within the passed in window.
  580. static    void    Wprintf( WindowRef window, char const *fmt, ... )
  581. {
  582.     char            s[4096];
  583.     va_list            args;
  584.     TXNObject        txnObject;
  585.     
  586.     if ( window == NULL )    return;
  587.     
  588.     va_start( args, fmt );
  589.     (void) vsprintf( s, fmt, args );
  590.     va_end( args );
  591.     
  592.     txnObject    = GetTXNObjectBySigAndID( window, 'HITv', 0 );
  593.     TXNSetData( txnObject, kTXNTextData, s, strlen(s), kTXNUseCurrentSelection, kTXNUseCurrentSelection );
  594.     return;
  595. }
  596.  
  597. //    Return the FSRef to the AppleScripts directory within our bundle
  598. static  OSStatus    GetAppleScriptsFolderFSRef( FSRef *fsRef)
  599. {
  600.     Boolean                        successful            = false;
  601.     CFURLRef                    urlRef1                = NULL;
  602.     CFURLRef                    urlRef2                = NULL;
  603.     
  604.     urlRef1 = CFBundleCopyResourcesDirectoryURL( CFBundleGetMainBundle() );                                    //    Get URL to our bundles "Resources" directory
  605.     require( urlRef1 != NULL , Bail );
  606.     urlRef2 = CFURLCreateCopyAppendingPathComponent( NULL, urlRef1, CFSTR("AppleScripts"), true );            //    Append "AppleScripts" onto the URL, location of our AppleScripts
  607.     require( urlRef1 != NULL , Bail );
  608.     successful  = CFURLGetFSRef( urlRef2, fsRef );                                                            //    Convert the CFURL into an FSRef to be returned
  609.     if ( urlRef1 != NULL )  CFRelease( urlRef1 );
  610.     if ( urlRef2 != NULL )  CFRelease( urlRef2 );
  611.  
  612. Bail:    
  613.     return( successful == false );
  614. }
  615.  
  616.  
  617. static  OSStatus    DisplayTextFileInWindow( WindowRef window, FSRef *fsRef )
  618. {
  619.     OSStatus                    status;
  620.     TXNObject                    txnObject;
  621.     FSSpec                        fsSpec;
  622.     CFURLRef                    cfURL                = NULL;
  623.     
  624.     require( (fsRef != NULL) && (window != NULL), Bail );
  625.  
  626.     txnObject   = GetTXNObjectBySigAndID( window, 'HITv', 0 );
  627.     require( txnObject != NULL, CantGetTXNObject );
  628.     
  629.     cfURL    = CFURLCreateFromFSRef( NULL, fsRef );                                            //  Create a CFURL from the passed in FSRef
  630.     require( cfURL != NULL, CantCreateURL );
  631.     status    = TXNSetDataFromCFURLRef( txnObject, cfURL, kTXNStartOffset, kTXNEndOffset );    //    url will be stored by MLTE
  632.     require_noerr( status, CantSetTXN );
  633.  
  634.     status    = FSGetCatalogInfo( fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL );            //  Need the FSSpec, since SetWindowProxy requires it
  635.     require_noerr( status, CantGetFSSpec );
  636.     SetWindowProxyFSSpec( window, &fsSpec );
  637.     
  638.     // The following lines deal with current shortcomings of the HITextView
  639.     TXNRevert( txnObject );                                                                    //  The change count is not set to 0 as it should,  a call to TXNRevert workarounds the problem
  640.     TXNSetSelection( txnObject, kTXNEndOffset, kTXNEndOffset );
  641.     TXNShowSelection( txnObject, true );                                                    //  The HIScrollView scroll bars don't reflect the current size of the HITextView, a couple of calls to Set/Show selection workaround the problem
  642.     TXNSetSelection( txnObject, kTXNStartOffset, kTXNStartOffset );
  643.     TXNShowSelection( txnObject, true );                                                    //  The HIScrollView scroll bars don't reflect the current size of the HITextView, a couple of calls to Set/Show selection workaround the problem
  644.  
  645. CantGetFSSpec:
  646. CantSetTXN:
  647. CantCreateURL:
  648.     if ( cfURL != NULL )        CFRelease( cfURL );
  649. CantGetTXNObject:
  650. Bail:
  651.     return( status );
  652. }
  653.  
  654.  
  655. /*****************************************************
  656. *
  657. * RunAppleScript( FSRef *scriptFSRef, char *textParameter )
  658. *
  659. * Purpose:  Runs an AppleScript with one text parameter as input.
  660. *            CreateMessageEvent, and therefore RunAppleScript, assumes the AppleScript has a
  661. *            subroutine entry titled "applescriptentry" and accepts one TEXT parameter.
  662. *
  663. * Inputs:   scriptFSRef        - FSRef to our AppleScript
  664. *            textParameter    - text parameter to our AppleScript 
  665. *
  666. * Returns:  OSStatus        - error code (0 == no error) 
  667. */
  668. static  OSStatus    RunAppleScript( FSRef *scriptFSRef, char *textParameter )
  669. {
  670.     OSStatus        err;
  671.     AppleEvent        aeParameter;
  672.     AEDesc            scriptData;
  673.     short            refNum;
  674.     FSCatalogInfo   catalogInfo;
  675.     Handle            h                = NULL;
  676.     
  677.     refNum  = FSOpenResFile( scriptFSRef, fsRdPerm );                                    //  Older (Mac OS 8/9) scripts store their data in the 'scpt' (1) resource
  678.     if ( refNum != -1 )
  679.     {
  680.         h        = Get1IndResource( 'scpt', 1 );
  681.         if( h != NULL )    DetachResource( h );                                            //    Detach the handle before closing the resource
  682.         CloseResFile( refNum );
  683.     }
  684.     if ( h == NULL )
  685.     {
  686.         err = FSGetCatalogInfo( scriptFSRef, kFSCatInfoDataSizes, &catalogInfo, NULL, NULL, NULL ); //  Get the size of the script
  687.         require_noerr( err, Bail );
  688.  
  689.         err = FSOpenFork( scriptFSRef, 0, NULL, fsRdPerm, &refNum );                    //  Open the data fork read only
  690.         require_noerr( err, Bail );
  691.  
  692.         h    = NewHandle( catalogInfo.dataLogicalSize );
  693.         err    = FSReadFork( refNum, fsFromStart, 0, catalogInfo.dataLogicalSize, *h, NULL );    //  Read the script into our handle
  694.         (void) FSCloseFork( refNum );                                                    //    Close the file reference
  695.     }
  696.  
  697.     err    = CreateMessageEvent( &aeParameter, textParameter );                                //  Create the AppleEvent, and use the Apple event to call the script's subroutine
  698.     require_noerr( err, Bail );
  699.  
  700.     err = AECreateDesc( typeOSAGenericStorage, *h, GetHandleSize(h), &scriptData );        //    Load the compiled script into an AEDesc of type typeOSAGenericStorage
  701.     require_noerr( err, Bail );
  702.                         
  703.     err    = ExecuteCompiledAppleScriptEvent( &scriptData, &aeParameter, NULL );            //  "Generic" routine to execute our AppleScript
  704.  
  705. Bail:
  706.     if ( h != NULL ) DisposeHandle( h );
  707.     return( err );
  708. }
  709.  
  710.  
  711. //    http://developer.apple.com/qa/qa2001/qa1111.html
  712. //    Creates an AppleEvent with one text parameter.  We leave it up to the AppleScript
  713. //    to further parse the text parameter into potentially more parameters.
  714. static  OSStatus    CreateMessageEvent( AppleEvent *theEvent, char *parameter )
  715. {
  716.     OSStatus                err;
  717.     ProcessSerialNumber     psn        = {0, kCurrentProcess};
  718.  
  719.     err    = AEBuildAppleEvent( 'ascr', kASSubroutineEvent, typeProcessSerialNumber, (Ptr) &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID,
  720.         theEvent,
  721.         NULL,
  722.         "'----':[TEXT(@)],"                //    One TEXT pointer parameter
  723.         "'snam':TEXT(@)",                //    The keyASSubroutineName ('snam') parameter must contain the name of the subroutine that is being called with every letter converted to lowercase. For example, if name of the subroutine in your script is "GetDocumentSize", then the string provided in the keyASSubroutineName parameter should be "getdocumentsize".
  724.         parameter, "applescriptentry");    //  The entry routine whithin the AppleScript
  725.  
  726.     return( err );
  727. }
  728.  
  729.  
  730.  
  731.